#include "epoll.h"
#include <sys/fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <system_error>
#include <string.h>
#include "socket.h"


epoll::epoll(int size)
    :fd(epoll_create(size))
{}

epoll::epoll(epoll &&e)
{
    fd = e.fd;
    e.fd = -1;
}

epoll::~epoll()
{
    if(fd != -1)
        close(fd);
}

void epoll::mount(int fd, int event_mask)
{
    int ret;
    ev.events = event_mask;
    ev.data.fd = fd;
    ret = epoll_ctl(this->fd, EPOLL_CTL_ADD, fd, &ev);
    if(ret == -1) throw std::system_error(errno,
                                          std::system_category(),
                                          std::string("epoll::mount() error: ") + strerror(errno));
}

void epoll::umount(int fd)
{
    int ret;
    ret = epoll_ctl(this->fd, EPOLL_CTL_DEL, fd, nullptr);
    if(ret == -1) throw std::system_error(errno,
                                          std::system_category(),
                                          std::string("epoll::umount() error: ") + strerror(errno));
}

void epoll::modify(int fd, int event_mask)
{
    int ret;
    ev.events = event_mask;
    ev.data.fd = fd;
    ret = epoll_ctl(this->fd, EPOLL_CTL_MOD, fd, &ev);
    if(ret == -1) throw std::system_error(errno,
                                          std::system_category(),
                                          std::string("epoll::modify() error: ") + strerror(errno));
}

std::tuple<size_t, epoll::event *> epoll::poll(int timeout_msec)
{
    int ret;
    ret = epoll_wait(this->fd, (struct epoll_event*)events, EPOLL_MAX_EVENTS, timeout_msec);
    if(ret == -1) throw std::system_error(errno,
                                          std::system_category(),
                                          std::string("epoll::poll() error: ") + strerror(errno));

    return std::make_tuple(ret, events);
}


epoll epoll::fromfd(int &&fd)
{
    return epoll(fd);
}

void epoll::mount(const class socket &sock, int event_mask)
{
    this->mount(sock.fileno(), event_mask);
}

void epoll::umount(const class socket &sock)
{
    this->umount(sock.fileno());
}

void epoll::modify(const class socket &sock, int event_mask)
{
    this->modify(sock.fileno(), event_mask);
}
